#!/usr/env/bin python
"""
:mod:`pynchon.core.object`
==========================

Provides array objects to store N-dimensional objects (tuples of objects).
"""

import numpy as np
from itertools import izip
from entity import EntityHierarchy, Entity_maker, Entity_sequencer


class Object(object):
    """
    Object interface class. This class provides methods specific to 
    N-dimensional objects e.g. strings of arbitrary length.
    
    Provides: 'raw' and 'is_valid' methods and the 'rank' attribute to
    subclasses. 
    """
    rank = 0

    @classmethod
    def is_valid_element(cls, element, metadata):
        """
        Verify if element is an instance of a valid object type.
        """
        if np.iterable(metadata['element_type']):
            if isinstance(element, (tuple, np.void)):
                if len(element) != len(metadata['element_type']):
                        raise ValueError("Element \"%s\" is incompatible with" \
                        " type: %s." % (element, metadata['element_type']))                   
                for e, etype in izip(element, metadata['element_type']):
                    if type(e) != etype:
                        raise ValueError("Element \"%s\" with sub-element of " \
                       "type %s is not of type: %s." % (element, type(e), metadata['element_type']))
            else:
                raise ValueError("Element \"%s\" is not a tuple, its "\
                                 "type should be: %s." % (element, metadata['element_type']))
        else:
            if type(element) != metadata['element_type']:
                raise ValueError("Element \"%s\" of type %s is not of type: " \
                      "%s." % (element, type(element), metadata['element_type']))

    @classmethod
    def is_valid_array(cls, array, metadata):
        """
        Verify if array contains elements of a valid object type.
        """             
        for element in array.flat:
            cls.is_valid_element(element, metadata)


    @classmethod
    def raw_sequence(cls, rank, dtype, fval, metadata, fast, kwargs, entities):
        """
        Import raw object
        """
        cls.is_valid_element(entities, metadata)
        try:
            entities = np.array(entities, dtype=dtype)
        except (ValueError, TypeError):
            raise ValueError("Expected a %sD sequence of Objects got: \"%s\"." \
                              % (len(dtype), entities))
        cls.is_valid_array(entities, metadata)
        return (entities,)


class ObjectVector(Object):
    """
    ObjectVector interface class. This class provides methods specific to
    vectors of N-dimensional Objects e.g. 0D - [obj1, obj2, ... , objN],
    1D - [(obj1,), (obj2,), ... (objN,)], 2D - [(obj1a, obj1b), (obj2a, obj2b), 
    ... , (obj3a, obj3b)].
    """
    rank = 1

    @classmethod
    def raw_sequence(cls, rank, dtype, fval, metadata, fast, kwargs, entities):
        return Entity_sequencer(cls, dtype, fval, metadata, entities)
    

class ObjectArray(ObjectVector):
    """
    ObjectArray interface class. This class provides methods specific to
    arrays of N-dimensional Objects. 
    """
    rank = 2


class ObjectCube(ObjectArray):
    """
    ObjectCube interface class. This class provides methods specific to
    cubes (3D) of N-dimensional Objects.
    """
    rank = 3


class ObjectHyperCube(ObjectCube):
    """
    ObjectHyperCube interface class. This class provides methods specific to
    cubes (4D) of N-dimensional Objects.
    """
    rank = 4


# create classes
classes = Entity_maker(base_classes=(Object, ObjectVector, ObjectArray, \
                                       ObjectCube, ObjectHyperCube),
                        dims=(0, 1, 2, 3, 4),
                        types=('object',)
                       )
# add to globals
for cls in classes:
    globals()[cls.__name__] = cls


#EOF